-- 辅助函数模块
-- 作者：失心

local functions = {}

------------------------------------------------------------------------------------------------------
--- shixinData 数据（相关）
---@class shixinDataSpec
---@field public general string @ 获得/失去的

---@class functions.shixinData: shixinDataSpec, TriggerData
functions.shixinData = TriggerData:subclass("evol_shixinData")

--- TriggerEvent
---@class functions.shixinTriggerEvent: TriggerEvent
---@field public data functions.shixinData
functions.shixinTriggerEvent = TriggerEvent:subclass("evol_shixinEvent")

--- 获得前
---@class functions.shixinBeforeAcquireDeputy: functions.shixinTriggerEvent
functions.shixinBeforeAcquireDeputy = functions.shixinTriggerEvent:subclass("fk.evol_shixinBeforeAcquireDeputy")

--- 失去前
---@class functions.shixinBeforeLoseDeputy: functions.shixinTriggerEvent
functions.shixinBeforeLoseDeputy = functions.shixinTriggerEvent:subclass("fk.evol_shixinBeforeLoseDeputy")

--- 获得后
---@class functions.shixinAcquireDeputy: functions.shixinTriggerEvent
functions.shixinAcquireDeputy = functions.shixinTriggerEvent:subclass("fk.evol_shixinAcquireDeputy")

--- 失去后
---@class functions.shixinLoseDeputy: functions.shixinTriggerEvent
functions.shixinLoseDeputy = functions.shixinTriggerEvent:subclass("fk.evol_shixinLoseDeputy")

---@alias shixinTrigFunc fun(self: TriggerSkill, event: functions.shixinTriggerEvent,
---  target: ServerPlayer, player: ServerPlayer, data: functions.shixinData):any

---@class SkillSkeleton
---@field public addEffect fun(self: SkillSkeleton, key: functions.shixinTriggerEvent,
---  data: TrigSkelSpec<shixinTrigFunc>, attr: TrigSkelAttribute?): SkillSkeleton

------------------------------------------------------------------------------------------------------
--- MainGeneralData 数据（相关）
---@class MainGeneralDataSpec
---@field public general string @ 获得/失去的

---@class functions.MainGeneralData: MainGeneralDataSpec, TriggerData
functions.MainGeneralData = TriggerData:subclass("evol_MainGeneralData")

--- TriggerEvent
---@class functions.MainGeneralTriggerEvent: TriggerEvent
---@field public data functions.MainGeneralData
functions.MainGeneralTriggerEvent = TriggerEvent:subclass("evol_MainGeneralEvent")

--- 获得前
---@class functions.MainGeneralBeforeAcquire: functions.MainGeneralTriggerEvent
functions.MainGeneralBeforeAcquire = functions.MainGeneralTriggerEvent:subclass("fk.evol_MainGeneralBeforeAcquire")

--- 失去前
---@class functions.MainGeneralBeforeLose: functions.MainGeneralTriggerEvent
functions.MainGeneralBeforeLose = functions.MainGeneralTriggerEvent:subclass("fk.evol_MainGeneralBeforeLose")

--- 获得后
---@class functions.MainGeneralAcquire: functions.MainGeneralTriggerEvent
functions.MainGeneralAcquire = functions.MainGeneralTriggerEvent:subclass("fk.evol_MainGeneralAcquire")

--- 失去后
---@class functions.MainGeneralLose: functions.MainGeneralTriggerEvent
functions.MainGeneralLose = functions.MainGeneralTriggerEvent:subclass("fk.evol_MainGeneralLose")

--- 变更前
---@class functions.MainGeneralBeforeChange: functions.MainGeneralTriggerEvent
functions.MainGeneralBeforeChange = functions.MainGeneralTriggerEvent:subclass("fk.evol_MainGeneralBeforeChange")

--- 变更后
---@class functions.MainGeneralAfterChange: functions.MainGeneralTriggerEvent
functions.MainGeneralAfterChange = functions.MainGeneralTriggerEvent:subclass("fk.evol_MainGeneralAfterChange")

---@alias MainGeneralTrigFunc fun(self: TriggerSkill, event: functions.MainGeneralTriggerEvent,
---  target: ServerPlayer, player: ServerPlayer, data: functions.MainGeneralData):any

---@class SkillSkeleton
---@field public addEffect fun(self: SkillSkeleton, key: functions.MainGeneralTriggerEvent,
---  data: TrigSkelSpec<MainGeneralTrigFunc>, attr: TrigSkelAttribute?): SkillSkeleton

------------------------------------------------------------------------------------------------------
--- GeneralTransformData 数据（相关）
---@class GeneralTransformDataSpec
---@field public from_general string @ 原始
---@field public to_general string @ 目标
---@field public reason? string @ 原因

---@class functions.GeneralTransformData: GeneralTransformDataSpec, TriggerData
functions.GeneralTransformData = TriggerData:subclass("evol_GeneralTransformData")

--- TriggerEvent
---@class functions.GeneralTransformTriggerEvent: TriggerEvent
---@field public data functions.GeneralTransformData
functions.GeneralTransformTriggerEvent = TriggerEvent:subclass("evol_GeneralTransformEvent")

--- 开始前
---@class functions.GeneralTransformBefore: functions.GeneralTransformTriggerEvent
functions.GeneralTransformBefore = functions.GeneralTransformTriggerEvent:subclass("fk.evol_GeneralTransformBefore")

--- 后
---@class functions.GeneralTransformAfter: functions.GeneralTransformTriggerEvent
functions.GeneralTransformAfter = functions.GeneralTransformTriggerEvent:subclass("fk.evol_GeneralTransformAfter")

--- 取消
---@class functions.GeneralTransformCancel: functions.GeneralTransformTriggerEvent
functions.GeneralTransformCancel = functions.GeneralTransformTriggerEvent:subclass("fk.evol_GeneralTransformCancel")

---@alias GeneralTransformTrigFunc fun(self: TriggerSkill, event: functions.GeneralTransformTriggerEvent,
---  target: ServerPlayer, player: ServerPlayer, data: functions.GeneralTransformData):any

---@class SkillSkeleton
---@field public addEffect fun(self: SkillSkeleton, key: functions.GeneralTransformTriggerEvent,
---  data: TrigSkelSpec<GeneralTransformTrigFunc>, attr: TrigSkelAttribute?): SkillSkeleton

------------------------------------------------------------------------------------------------------

-- 根据字符查找并选择技能
-- 参数：
--   char: 要查找的字符（数字或字母）
--   player: 当前玩家
--   room: 游戏房间
--   x: 随机选取的候选技能数量
--   y: 最终选择的技能数量
--   skill_name: 调用此函数的技能名称
-- 返回值：
--   如果选择了技能，返回技能名称列表；否则返回空表
function functions.findRandomSkillByChar(char, player, room, x, y, skill_name)
  local available_skills = {}
  
  for general_name, general in pairs(Fk.generals) do
    -- 排除国战武将：检查武将名是否包含国战前缀
    local is_hegemony_general = general_name:find("ty_heg__") or 
                                general_name:find("os_heg__") or 
                                general_name:find("zq_heg__") or 
                                general_name:find("jy_heg__") or 
                                general_name:find("hs__") or
                                general_name:find("heg__")
    
    if not is_hegemony_general then
      for _, skill_id in ipairs(general:getSkillNameList()) do
        local skill = Fk.skills[skill_id]
        if skill and 
           not skill_id:startsWith("#") and     
           not skill_id:find("heg__") and     
           not skill.attached_equip and           
           not player:hasSkill(skill_id) then  
          local desc = Fk:translate(":" .. skill_id)
          if desc then
            if (char:match("%a") and desc:lower():find(char:lower())) or
               (char:match("%d") and desc:find(char)) then
              table.insert(available_skills, {name = skill_id, desc = desc})
            end
          end
        end
      end
    end
  end
  
  -- 去重
  local unique_skills = {}
  for _, skill in ipairs(available_skills) do
    unique_skills[skill.name] = skill
  end
  
  available_skills = {}
  for skill_name, skill in pairs(unique_skills) do
    table.insert(available_skills, skill)
  end
  
  -- 如果候选技能不足，直接返回所有技能
  x = math.min(x, #available_skills)
  
  if x > 0 then
    -- 随机选择x个候选技能
    local candidates = {}
    local shuffled = {}
    
    -- 手动复制表
    for _, skill in ipairs(available_skills) do
      table.insert(shuffled, skill)
    end
    
    table.shuffle(shuffled)
    
    for i = 1, x do
      table.insert(candidates, shuffled[i])
    end
    
    -- 让玩家从候选技能中选择y个
    local choices = {}
    local choice_map = {}
    
    for i, skill in ipairs(candidates) do
      local choice_text = skill.name
      table.insert(choices, choice_text)
      choice_map[choice_text] = skill.name
    end
    
    local selected_skills = {}
    for i = 1, y do
      if #choices == 0 then break end
      
      local choice = room:askToChoice(player, {
        choices = choices,
        skill_name = skill_name,
        prompt = "请选择要获得的技能",
        detailed = true
      })
      local selected_skill = choice_map[choice]
      
      table.insert(selected_skills, selected_skill)
      
      -- 从候选列表中移除已选技能
      for j = #choices, 1, -1 do
        if choice_map[choices[j]] == selected_skill then
          table.remove(choices, j)
          break
        end
      end
    end
    return selected_skills
  end
  
  return {}
end






-- 重铸技能
-- 参数：
--   player: 玩家
--   room: 游戏房间
--   skill_name: 触发重铸的技能名称
--   prompt_remove: 选择移除技能的提示
--   prompt_gain: 选择获得技能的提示
--   num_choices: 提供多少个选择，默认3个
-- 返回值：
--   成功重铸返回true，否则返回false
function functions.reforgeSkill(player, room, skill_name, prompt_remove, prompt_gain, num_choices)
  num_choices = num_choices or 3
  
  -- 获取玩家当前所有技能
  local skills = {}
  for _, skill in ipairs(player.player_skills) do
    if not skill.attached_equip and not skill.name:startsWith("#") then
      table.insert(skills, skill.name)
    end
  end
  
  -- 如果没有可移除的技能，返回失败
  if #skills == 0 then return false end
  
  -- 询问玩家选择要移除的技能
  local to_remove = room:askToChoice(player, {
    choices = skills,
    skill_name = skill_name,
    prompt = prompt_remove,
    detailed = true
  })
  if not to_remove then return false end
  
  -- 移除选择的技能
  room:handleAddLoseSkills(player, "-"..to_remove, nil)
  
  -- 获取所有可选技能
  local all_skills = {}
  for _, general in ipairs(Fk:getAllGenerals()) do
    for _, skill_id in ipairs(general:getSkillNameList()) do
      local skill = Fk.skills[skill_id]
      if skill and not player:hasSkill(skill_id) and 
         not skill_id:startsWith("#") and
         not skill.attached_equip then
        table.insertIfNeed(all_skills, skill_id)
      end
    end
  end
  
  -- 如果没有可选择的新技能，返回部分成功
  if #all_skills == 0 then return true end
  
  -- 随机选取指定数量的技能
  local choices = table.random(all_skills, math.min(num_choices, #all_skills))
  
  -- 询问玩家选择要获得的技能
  local new_skill = room:askToChoice(player, {
    choices = choices,
    skill_name = skill_name,
    prompt = prompt_gain,
    optional = true,
    detailed = true
  })
  
  -- 玩家获得新技能
  if new_skill then
    room:handleAddLoseSkills(player, new_skill, nil)
  end
  
  return true
end








-- 判断两个角色之间是否为敌对关系
-- 参数：
--   from: 判断的基准角色
--   to: 需要判断敌友关系的目标角色
-- 返回值：
--   true: 敌对关系
--   false: 友方关系
function functions.isEnemy(from, to)
  if from.id == to.id then return false end -- 自己不是敌人
  
  if from.role == "lord" or from.role == "loyalist" then
    return (to.role ~= "lord" and to.role ~= "loyalist")
  elseif from.role == "rebel" then
    return (to.role == "lord" or to.role == "loyalist")
  elseif from.role == "renegade" then
    return true -- 内奸视所有其他角色为敌人
  end
  
  return false -- 默认不是敌人
end

-- 判断两个角色之间是否为友方关系
-- 参数：
--   from: 判断的基准角色
--   to: 需要判断敌友关系的目标角色
-- 返回值：
--   true: 友方关系
--   false: 敌对关系
function functions.isFriend(from, to)
  return not functions.isEnemy(from, to)
end








-- 移除角色的所有技能并临时保存
-- 参数：
--   player: 要移除技能的玩家
--   room: 游戏房间
--   tag_name: 用于存储移除技能的标签名称，默认为"removed_skills"
--   mark_name: 用于设置标记的名称，默认为nil（不设置标记）
-- 返回值：
--   移除的技能列表
function functions.removeAllSkills(player, room, tag_name, mark_name)
  tag_name = tag_name or "removed_skills"
  
  -- 收集所有可见的非装备技能
  local skills = {}
  for _, skill in ipairs(player.player_skills) do
    if skill.visible and 
       not skill.name:startsWith("#") and 
       not skill.name:endsWith("&") and 
       not skill.cardSkill and 
       not skill:isEquipmentSkill(player) then
      table.insertIfNeed(skills, skill.name)
    end
  end
  
  -- 保存要移除的技能列表
  player.tag[tag_name] = skills
  
  -- 移除所有技能
  for _, skill_name in ipairs(skills) do
    room:handleAddLoseSkills(player, "-" .. skill_name, nil, false, true)
  end
  
  -- 如果提供了标记名称，设置标记
  if mark_name then
    room:setPlayerMark(player, mark_name, 1)
  end
  
  return skills
end



-- 恢复之前移除的技能
-- 参数：
--   player: 要恢复技能的玩家
--   room: 游戏房间
--   tag_name: 存储移除技能的标签名称，默认为"removed_skills"
--   mark_name: 要清除的标记名称，默认为nil（不清除标记）
-- 返回值：
--   恢复的技能列表
function functions.restoreRemovedSkills(player, room, tag_name, mark_name)
  tag_name = tag_name or "removed_skills"
  
  -- 获取保存的技能列表
  local skills = player.tag[tag_name] or {}
  
  -- 恢复所有技能
  for _, skill_name in ipairs(skills) do
    room:handleAddLoseSkills(player, skill_name, nil, false, true)
  end
  
  -- 清除标签
  player.tag[tag_name] = nil
  
  -- 如果提供了标记名称，清除标记
  if mark_name then
    room:setPlayerMark(player, mark_name, 0)
  end
  
  return skills
end








-- 将为另一个武将
-- 参数：
--   player: 要变身的玩家
--   room: 游戏房间
--   from_general: 原始武将名称，如果为nil则不检查原始武将
--   to_general: 变身后的武将名称
--   log_type: 发送日志的类型，默认为nil（不发送日志）
--   skills: 变身后要获得的技能列表，用|分隔，默认为nil（不获得技能）
-- 返回值：
--   true: 变身成功
--   false: 变身失败（未找到匹配的武将）
function functions.transformGeneral(player, room, from_general, to_general, log_type, skills)
  local transformed = false
  
  -- 检查
  if (from_general == nil or player.general == from_general) and not transformed then
    player.general = to_general
    room:broadcastProperty(player, "general")
    transformed = true
  -- 检查
  elseif (from_general == nil or player.deputyGeneral == from_general) and not transformed then
    player.deputyGeneral = to_general
    room:broadcastProperty(player, "deputyGeneral")
    transformed = true
  end
  
  -- 如果变身成功且需要发送日志
  if transformed and log_type then
    room:sendLog{
      type = log_type,
      from = player.id,
    }
  end
  
  -- 如果变身成功且需要获得技能
  if transformed and skills then
    room:handleAddLoseSkills(player, skills, nil)
  end
  
  return transformed
end







-- 处理装备触发的变身效果
-- 参数：
--   install: 是否为安装装备（true表示安装，false表示卸下）
--   player: 玩家对象
--   room: 游戏房间
--   general_name: 变身后的武将名称（安装时）或原始武将名称（卸下时）
--   tag_name: 用于存储原始武将的标签名
--   transform_log: 变身时显示的日志
--   restore_log: 恢复时显示的日志
--   skills: 获得的技能，用|分隔（安装时）或需要移除的技能（卸下时）
-- 返回值：
--   true: 操作成功
--   false: 操作失败
function functions.transformEquip(install, player, room, general_name, tag_name, transform_log, restore_log, skills)
  if install then
    -- 保存原始武将
    player.tag[tag_name] = player.general
    -- 变身为新武将
    local transformed = functions.transformGeneral(player, room, nil, general_name, transform_log, skills)
    return transformed
  else
    -- 恢复原始武将
    local original_general = player.tag[tag_name]
    if original_general then
      local transformed = functions.transformGeneral(player, room, general_name, original_general, restore_log, nil)
      player.tag[tag_name] = nil
      
      -- 移除技能
      if skills and skills ~= "" then
        room:handleAddLoseSkills(player, "-"..skills, nil, true, false)
      end
      
      return transformed
    end
    return false
  end
end







-- 从指定的拓展包中获取随机技能
-- 参数：
--   player: 玩家，用于检查是否已拥有技能
--   packages: 拓展包名称或拓展包名称的表（可以是一个或多个）
--   num: 要返回的技能数量，默认为2
--   filter_func: 可选的过滤函数，接受技能名和技能对象作为参数，返回true表示保留该技能
-- 返回值：
--   如果找到足够的技能，返回技能名称的表；否则返回nil
function functions.getRandomSkillsFromPackages(player, packages, num, filter_func)
  local room = player.room
  num = num or 2
  
  -- 确保packages是一个表
  if type(packages) == "string" then
    packages = {packages}
  end
  
  local skill_packages = {}
  for _, pkg_name in ipairs(packages) do
    if Fk.packages[pkg_name] then
      table.insert(skill_packages, Fk.packages[pkg_name])
    end
  end
  
  -- 如果没有找到任何拓展包，返回nil
  if #skill_packages == 0 then
    return nil
  end
  
  local available_skills = {}
  for _, pkg in ipairs(skill_packages) do
    for _, general in pairs(pkg.generals) do
      for _, skill_id in ipairs(general:getSkillNameList()) do
        local skill = Fk.skills[skill_id]
        if skill and not string.find(skill_id, "^#") and     -- 非隐藏技能
           not skill:isEquipmentSkill(player) and               -- 非装备技能
           not skill.cardSkill and                              -- 非卡牌技能
           not player:hasSkill(skill_id) then                 -- 玩家尚未拥有
          
          -- 如果提供了过滤函数，使用它进行额外的过滤
          if not filter_func or filter_func(skill_id, skill) then
            table.insert(available_skills, skill_id)
          end
        end
      end
    end
  end
  
  -- 去重
  local unique_skills = {}
  for _, skill_id in ipairs(available_skills) do
    unique_skills[skill_id] = true
  end
  
  available_skills = {}
  for skill_id, _ in pairs(unique_skills) do
    table.insert(available_skills, skill_id)
  end
  
  -- 如果可用技能数量不足，返回nil
  if #available_skills < num then
    return nil
  end
  
  -- 随机选择指定数量的技能
  return table.random(available_skills, num)
end







-- 相关辅助函数

--- 变更
---@param player ServerPlayer 玩家
---@param new_general string 新的名称
---@param reason? string 变更原因
functions.changeMainGeneral = function(player, new_general, reason)
  local room = player.room
  local old_general = player.general
  
  -- 触发变更前事件
  local data = { general = new_general }
  room.logic:trigger(functions.MainGeneralBeforeChange, player, data)
  
  -- 变更
  player.general = new_general
  room:broadcastProperty(player, "general")
  
  -- 触发变更后事件
  room.logic:trigger(functions.MainGeneralAfterChange, player, { 
    general = new_general,
    old_general = old_general
  })
  
  room:sendLog{
    type = "#MainGeneralChanged",
    from = player.id,
    arg = new_general,
    arg2 = old_general,
    toast = true,
  }
end

------------------------------------------------------------------------------------------------------

-- evol_advance 功能 - 隐匿功能

--- 进入隐匿状态
---@param player ServerPlayer 要进入隐匿状态的玩家
---@param reason string 隐匿原因
function functions.evol_advance_enter_hidden(player, reason)
  local room = player.room
  
  -- 设置隐匿标记
  room:setPlayerMark(player, "__evol_advance_hidden", 1)
  
  -- 设置隐匿原因标记
  if reason then
    room:setPlayerMark(player, "__evol_advance_hidden_reason", reason)
  end
  
  -- 隐藏玩家（使其不可见）
  room:setPlayerProperty(player, "visible", false)
  
  -- 发送隐匿日志
  room:sendLog{
    type = "#EvolAdvanceHidden",
    from = player.id,
    arg = reason or "jin",
    toast = true,
  }
  
  return true
end

--- 退出隐匿状态
---@param player ServerPlayer 要退出隐匿状态的玩家
function functions.evol_advance_exit_hidden(player)
  local room = player.room
  
  -- 检查是否处于隐匿状态
  if player:getMark("__evol_advance_hidden") == 0 then
    return false
  end
  
  -- 清除隐匿标记
  room:removePlayerMark(player, "__evol_advance_hidden")
  room:removePlayerMark(player, "__evol_advance_hidden_reason")
  
  -- 恢复可见性
  room:setPlayerProperty(player, "visible", true)
  
  -- 发送退出隐匿日志
  room:sendLog{
    type = "#EvolAdvanceVisible",
    from = player.id,
    toast = true,
  }
  
  return true
end

--- 检查是否处于隐匿状态
---@param player ServerPlayer 要检查的玩家
---@return boolean 是否处于隐匿状态
function functions.evol_advance_is_hidden(player)
  return player:getMark("__evol_advance_hidden") > 0
end

--- 获取隐匿原因
---@param player ServerPlayer 要检查的玩家
---@return string 隐匿原因
function functions.evol_advance_get_hidden_reason(player)
  return player:getMark("__evol_advance_hidden_reason") or ""
end

--- 获得
---@param player ServerPlayer 玩家
---@param general? string 获得的名称
functions.acquireMainGeneral = function(player, general)
  local room = player.room
  local data = { general = general }
  
  -- 触发获得前事件
  room.logic:trigger(functions.MainGeneralBeforeAcquire, player, data)
  
  if data.general then
    -- 触发获得后事件
    room.logic:trigger(functions.MainGeneralAcquire, player, { general = data.general })
    
    room:sendLog{
      type = "#MainGeneralAcquired",
      from = player.id,
      arg = data.general,
      toast = true,
    }
  end
end

--- 失去
---@param player ServerPlayer 玩家
---@param general? string 失去的名称
functions.loseMainGeneral = function(player, general)
  local room = player.room
  local data = { general = general }
  
  -- 触发失去前事件
  room.logic:trigger(functions.MainGeneralBeforeLose, player, data)
  
  if data.general then
    -- 触发失去后事件
    room.logic:trigger(functions.MainGeneralLose, player, { general = data.general })
    
    room:sendLog{
      type = "#MainGeneralLost",
      from = player.id,
      arg = data.general,
      toast = true,
    }
  end
end

-- 相关辅助函数

--- 触发事件
---@param player ServerPlayer 玩家
---@param from_general string 原始武将
---@param to_general string 目标武将
---@param reason? string 变身原因
functions.triggerGeneralTransform = function(player, from_general, to_general, reason)
  local room = player.room
  local data = {
    from_general = from_general,
    to_general = to_general,
    reason = reason
  }
  
  -- 触发变身开始前事件
  room.logic:trigger(functions.GeneralTransformBefore, player, data)
  
  -- 如果事件没有被取消，触发变身后事件
  if not data.cancelled then
    room.logic:trigger(functions.GeneralTransformAfter, player, data)
    
    room:sendLog{
      type = "#GeneralTransformed",
      from = player.id,
      arg = to_general,
      arg2 = from_general,
      toast = true,
    }
  else
    -- 触发变身取消事件
    room.logic:trigger(functions.GeneralTransformCancel, player, data)
  end
end





-- 加载翻译表
Fk:loadTranslationTable{
  ["#MainGeneralChanged"] = "%from 的从 %arg2 变更为 %arg",
  ["#MainGeneralAcquired"] = "%from 获得了 %arg",
  ["#MainGeneralLost"] = "%from 失去了 %arg",
  ["#GeneralTransformed"] = "%from 的武将从 %arg2 变身为 %arg",
  ["#shixinLoseDeputy"] = "%from 失去了 %arg",
  ["#shixinAcquireDeputy"] = "%from 获得了 %arg",
}

return functions

